# -*- coding:utf-8 -*-

# @Time    : 2023/9/29 23:49
# @Author  : zengwenjia
# @Email   : zengwenjia@lingxi.ai
# @Software: LLM_internal

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
import asyncio

from bot.insurance_planner_gpt.agent.base_agent import LLMAgent
from knowledge_base.knowledge_embedding import BGEQuery
from bot.insurance_planner_gpt.agent.insurance_underwriting import InsuranceUnderwriting

default_template = """
作为一个最专业规划师的解决方案构建agent,你需要基于从用户目标出发的提案和全面客观的优劣对比，辅助用户完成整个方案的各个要素的选择决策，根据和用户的对话上下文，理解用户问题，结合方案知识里的信息，一步步思考，完善基于用户个人需求的因果图。
需要注意以下情况：
1、该因果图是用于表示基于用户需求的保险方案，因此仅根据给的用户需求（用户问题或用户信息）进行展开，不要无中生有，因果图由因果链构成，因果链由用户需求开始，只能由保险类型（医疗险、重疾险等）、保险产品类型（百万医疗、中端医疗等）、保险产品名称、保险条款等保险相关方案结束，中间通过"->"连接因果关系
2、"->"的左边是因，右边是果，由于是因果关系，一定是左边的变化会引起右边的变化，右边的产品是解决左边的需求，参照背景知识，不要随意颠倒因果关系，注意不同保险产品间的区别，不要有错误的匹配（如不要把中高端医疗产品匹配到百万医疗需求），保险产品必须依赖保险知识，严格从【方案知识】中选择，切记不要无中生有
3、输出的结果需要注明保险类型、保险产品类型、保险产品名称，注明内容必须参照方案知识中的内容，不要无中生有
4、如果用户需求不明确，无法匹配保险解决方案，生成因果图的，则输出"无"
5、检索出的【方案知识】中是分层级的，在和用户沟通规划的不同阶段，对于知识的使用如下：
- step1：首先需要根据需求明确保险类型/保险产品类型，需要结合【阶段一】的知识构建对应的因果链，因果链以对应保险类型/保险产品类型结束，不要使用阶段二和阶段三的知识
- step2：在用户明确保险类型后，使用【阶段二】的给出保险类型里的重要参数的选择逻辑，主要包括保障顺序建议、保费预算建议、缴纳期数建议等；根据具体沟通进展，step2可以跳过，直接进入step3
- step3：明确用户需要的保险类型后，根据用户的需求好偏好，使用【阶段三】的知识找到对应类型下的具体的保险产品名称，以及相关的条款说明；注意，如果当前和用户的对话并没有明确【阶段一】的保险类型，则直接跳过阶段三，不要使用阶段三的知识，即不要推荐具体产品
6、解决方案需要逐步由粗颗粒度到细颗粒度，逐步收集必要信息，明确用户的需求，再一步步给出解决该问题的保险类型、保险产品类型、产品名、产品条款；因此若用户还未了解保险类型则不要输出产品类型，更不要直接匹配具体的产品名
7、如果截止目前已经和用户确认了需要的保险产品类型，接下来需要推荐产品时，必须在【方案知识】里找到所有类型下建议的产品，如果没有合适的产品，则写'超出范围'
8、给出的解决方案尽可能覆盖用户所有的问题,也要结合当前用户情境，不要给出不合适的因果图；
9、在输出本次因果图前先参考历史的因果图，如果对话导致历史因果图有变化则更新历史因果图，否则只根据最新对话补充新的因果图，最终只输出整合后的因果图结果。
10、目前你只具备推荐在已有知识库里规划保险的能力，因此你只能在以下范围内完善因果图，超出范围的可以写'超出范围'：
- 保险类型只能是：医疗险、重疾险、意外险、定期寿险、增额终身寿险、终身寿险、养老年金险、教育年金险、旅意险
- 保险产品类型只能是：门诊险、百万医疗、中端医疗、高端医疗、重疾险、意外险、定期寿险、增额终身寿险、终身寿险、境内旅行、境外旅行、运动赛事
- 保险产品名称只能是：从【方案知识】里获取的保险产品名称
11、【阶段三】里会存在 '无效方案'，这是因为当前被保人的情况不符合对应方案里的保险产品，比如年龄不符，此时输出的最终方案需要根据用户实际问题判别是否保留该条内容：
- 如果用户是要我们规划保险，那么无效方案需要剔除
- 但是用户自己问到该保险产品，那么对应该产品的方案需要保留，并且要给出无效方案（即当前被保人无法投保）的原因，以及该产品的具体名称
12、你给出的方案得是有针对性且随着因果链递进的，不要给出冗余的方案，比如已经讲解完重疾险要给用户推产品了，那么就整合【方案知识】三个阶段的逻辑写成一条递进式链路
- 比如'保险规划_给本人的保险->重疾险（保险类型）->i无忧重疾险（保险产品名称）'
13、如果【阶段三】给出了产品名称，但是还没和用户沟通确认保险产品类型，那么因果链一定不能出现保险产品名称，而应该以保险类型或保险产品类型结束。
14、你输出的只能是因果链，或者’无‘，不要有多余的解释说明
15、【阶段三】里的条款知识不能篡改，生成的因果链中如有提到则必须一致，比如'少儿门急诊'不能写成'门急诊'，这两个就不是一个含义了
16、只要因果链中包含保险产品名称，就必须在（保险产品名称）后加上对应的保险类型和保险产品类型


{user_info}
{user_question}
{condidate_products}
{solutions_infos}
{knowledge_infos}
{product_causal_solution_cache}
对话上下文信息：
===
{conversation_history}
===
根据对话用户信息、背景因果图和历史因果图，参照背景知识里因果图的格式，请一步步思考，基于当前对话的用户问题，整体保险方案的因果图是:
"""


class CausalSolution(LLMAgent):
    def __init__(self, user_question, user_info, condidate_products, knowledge_infos, solutions_infos, conversation_history, product_causal_solution_cache):
        self.knowledge_infos = "\n\n【相关保险知识】：\n" + "\n".join(
            [str(knowledge_info) for knowledge_info in knowledge_infos]) if knowledge_infos else ""
        self.solutions_infos = "\n\n【方案知识】：\n" + "\n".join(
            [str(solutions_info) for solutions_info in solutions_infos]) if solutions_infos else ""
        self.user_info = "\n【当前用户情境】：" + str(user_info) if user_info else ""
        self.user_question = "\n\n【用户问题列表】：\n" + user_question if user_question else ""
        # self.condidate_products = "\n\n【可能关注保险产品列表】：\n" + str(condidate_products) if condidate_products else ""
        self.condidate_products = ""
        self.product_causal_solution_cache = "\n\n【历史的因果图为】：\n" + product_causal_solution_cache if product_causal_solution_cache else ""
        self.prompt = default_template.format(user_question=self.user_question,
                                              user_info=self.user_info,
                                              condidate_products=self.condidate_products,
                                              solutions_infos=self.solutions_infos,
                                              knowledge_infos=self.knowledge_infos,
                                              product_causal_solution_cache=self.product_causal_solution_cache,
                                              conversation_history=conversation_history)
        super().__init__(self.prompt)


if __name__ == "__main__":
    user_question_str = "小神童3号介绍下"
    # conversation_history = """
    # 犀心小助:嘿！你好呀，我是犀心小助👋，很高兴遇见你，我是你的专属保险规划师，我在这里为你提供全面的保险规划服务，解答你的所有保险相关问题。请随时把你的问题告诉我，我会竭尽全力帮你找到适合你的解决方案哦！😊有什么可以帮助到你的吗？
    # 用户: 小团圆保险怎么样啊
    #     """
    conversation_history=""
    # user_info = {'本人年龄': 40, '本人性别': '男', '是否已结婚': '是', '被保人': '本人', '配偶年龄': 45, '配偶性别': '女', '孩子个数': 1, '人生阶段': '生育期',
    #              '当前年收入': 0, '当前年支出': 0, '年保费预算': 10000}

    user_info = {'被保人':'本人'}

    knowledge_base_query = BGEQuery()
    knowledge_base_query.load_all_knowledge_base()
    user_question_list = user_question_str.split("\n")
    knowledge_infos = []
    for user_question in user_question_list:
        results = knowledge_base_query.search_product_with_score(user_question, top_k=8)
        if results:
            for result in results:
                knowledge_infos.append(result.metadata)
    print(knowledge_infos)
    knowledge_infos = [] # 不传入保险知识了

    # 方案（因果图）构建
    # step1：用户问题获取方案知识
    solutions_infos = []
    quesitons = user_question.split("\n")
    from bot.insurance_planner_gpt.utils.causal_data_process_util import CausalDataProcessUtil
    causalDataProcessUtil = CausalDataProcessUtil()
    insurance_underwriting = InsuranceUnderwriting()

    for question in quesitons:
        if question:
            raw_solutions = knowledge_base_query.search_solutions(question, top_k=8, if_print_score=True)
            if raw_solutions:
                # step2：根据情境找到合适的方案（留下合适的top产品，入果不能买，给出不能买原因）
                insurance_person_info = asyncio.run(insurance_underwriting.get_insurance_person_info(user_info))
                match_solution_infos = asyncio.run(causalDataProcessUtil.match_solution_info(user_info,
                                                                                            insurance_person_info,
                                                                                            raw_solutions, top_k=5))
                solutions_infos = solutions_infos + match_solution_infos

    result_gpt = CausalSolution(user_question_str, user_info, knowledge_infos, solutions_infos,conversation_history, "")
    result = asyncio.run(result_gpt.achat_auto_llm(type="gpt"))
    print(result)
